#pragma once
#include "glog/logging.h"
#include <memory>
#include <mutex>
#include <string>
#include <ucontext.h>
#include <vector>

extern "C" {
void CoHelper(void);
};

namespace coroutine {
// constexpr size_t STACK_SIZE = 128 * 1024;
constexpr size_t STACK_SIZE = 2 * 1024 * 1024;
constexpr size_t MAX_COROUTINE_NUMBER = 64;
typedef void (*CoFunc)(void *arg);

enum class CoroutineState {
  CO_READY,
  CO_FINISHED,
  CO_RUNNING,
  CO_SUSPEND,
};
struct CoroutineContext {
  ucontext_t ctx;
  CoFunc func;
  void *arg;
  bool force_stop = false;
  int id;
  CoroutineState state = CoroutineState::CO_FINISHED;
  std::string name;
  std::unique_ptr<char[]> stack;
};
using UContextPtr = std::shared_ptr<CoroutineContext>;

class Coroutine;
class Schedule {
public:
  static Schedule *Instance() {
    static std::once_flag s_flag;
    std::call_once(s_flag, [&]() { schedule.reset(new Schedule); });
    return schedule.get();
  }
  void Resume(int id);
  void Yield();
  int Finish();
  int CreateCoroutine(const std::string &name, CoFunc func, void *arg);
  UContextPtr GetCurrentCoroutine() {
    if (Instance()->runing_coroutine_id_ != -1) {
      return Instance()->coroutines_[Instance()->runing_coroutine_id_];
    }
    return nullptr;
  }
  void SetWorkId(int id) { runing_coroutine_id_ = id; }

private:
  Schedule();
  Schedule(const Schedule &) = delete;
  Schedule &operator=(const Schedule &) = delete;

  UContextPtr GetCoroutineContext();
  static std::unique_ptr<Schedule> schedule;
  ucontext_t main_;
  int runing_coroutine_id_ = -1;
  std::vector<UContextPtr> coroutines_;
};

} // namespace coroutine

